package ai_bloomfilter

import (
	"math"
	"github.com/spaolacci/murmur3"
	"fmt"
)

type BitOperation interface {
	SetBit(string, int64, int) (int64, error)
	GetBit(string, int64) (int64, error)
}

type AIBloomFilter struct {
	Capacity  int			// 容量
	ErrorRate float64		// 容错率
	RdsConn   BitOperation	// bit操作接口
	Seeds     []uint32		// 随机种子
	m         float64		// 总bit位数
	k         int			// hash次数
	mem       int			// 耗费内存(M)
	blockNum  int			// 内存块数量(512M一块内存)
	N         uint32		// 2^32-1
	Key       string		// 去重的key
}

func (aiBFilter *AIBloomFilter) Init(capacity int, rate float64, conn BitOperation, key string) error {
	Seeds := [100]uint32{543, 460, 171, 876, 796, 607, 650, 81, 837, 545, 591, 946, 846, 521, 913, 636, 878, 735, 414, 372,
		344, 324, 223, 180, 327, 891, 798, 933, 493, 293, 836, 10, 6, 544, 924, 849, 438, 41, 862, 648, 338,
		465, 562, 693, 979, 52, 763, 103, 387, 374, 349, 94, 384, 680, 574, 480, 307, 580, 71, 535, 300, 53,
		481, 519, 644, 219, 686, 236, 424, 326, 244, 212, 909, 202, 951, 56, 812, 901, 926, 250, 507, 739, 371,
		63, 584, 154, 7, 284, 617, 332, 472, 140, 605, 262, 355, 526, 647, 923, 199, 518}
	aiBFilter.Capacity = capacity
	aiBFilter.ErrorRate = rate
	aiBFilter.RdsConn = conn

	// 需要的总bit位数
	aiBFilter.m = math.Ceil(float64(capacity) * math.Log2(math.E) * math.Log2(1 / rate))
	// 需要最少的hash次数
	aiBFilter.k = int(math.Ceil(math.Log1p(2) * aiBFilter.m / float64(capacity)))
	aiBFilter.Seeds = Seeds[0:aiBFilter.k]
	// 需要的多少M内存
	aiBFilter.mem = int(math.Ceil(aiBFilter.m / 8 / 1024 / 1024))
	// offset 参数必须大于或等于 0 ，小于 2^32 (bit 映射被限制在 512 MB 之内)。
	aiBFilter.N = 2 << 31 - 1
	// 需要多少个512M的内存块,value的第一个字符必须是ascii码，所有最多有256个内存块
	aiBFilter.blockNum = int(math.Ceil(float64(aiBFilter.mem) / 512))
	if key == "" {
		key = "ai_bloom_filter"
	}
	aiBFilter.Key = key
	return nil
}

func (aiBFilter *AIBloomFilter) GetHashValues(value string) []uint32 {
	var hashValues []uint32
	for _, seed := range aiBFilter.Seeds {
		hashValues = append(hashValues, murmur3.Sum32WithSeed([]byte(value), seed))
	}
	return hashValues
}

func (aiBFilter *AIBloomFilter) IsExists(key, value string) (bool, error) {
	if key == "" {
		key = "ai_bloom_filter"
	}
	name := fmt.Sprintf("%s_%d", key, int([]rune(value)[0]) % aiBFilter.blockNum)
	var exists bool
	exists = true
	hashValues := aiBFilter.GetHashValues(value)
	for _, hashValue := range hashValues {
		res, err := aiBFilter.RdsConn.GetBit(name, int64(hashValue))
		if err != nil {
			return false, err
		}
		if res == 0 {
			exists = false
			break
		}
	}
	return exists, nil
}

func (aiBFilter *AIBloomFilter) Add(key, value string) error {
	if key == "" {
		key = "ai_bloom_filter"
	}
	name := fmt.Sprintf("%s_%d", key, int([]rune(value)[0]) % aiBFilter.blockNum)
	hashValues := aiBFilter.GetHashValues(value)
	for _, hashValue := range hashValues {
		_, err := aiBFilter.RdsConn.SetBit(name, int64(hashValue), 1)
		if err != nil {
			return err
		}
	}
	return nil
}

func (aiBFilter *AIBloomFilter) Remove(key, value string) error {
	if key == "" {
		key = "ai_bloom_filter"
	}
	name := fmt.Sprintf("%s_%d", key, int([]rune(value)[0]) % aiBFilter.blockNum)
	hashValues := aiBFilter.GetHashValues(value)
	for _, hashValue := range hashValues {
		_, err := aiBFilter.RdsConn.SetBit(name, int64(hashValue), 0)
		if err != nil {
			return err
		}
	}
	return nil
}


